package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.TbPullNewDetailAddDTO;
import info.batcloud.fanli.core.dto.TbPullNewDetailDTO;
import info.batcloud.fanli.core.entity.TbPullNewDetail;
import info.batcloud.fanli.core.entity.User;
import info.batcloud.fanli.core.helper.PagingHelper;
import info.batcloud.fanli.core.repository.TbPullNewDetailRepository;
import info.batcloud.fanli.core.repository.UserRepository;
import info.batcloud.fanli.core.service.SystemSettingService;
import info.batcloud.fanli.core.service.TbPullNewDetailService;
import info.batcloud.fanli.core.settings.ActivitySetting;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

@Service
public class TbPullNewDetailServiceImpl implements TbPullNewDetailService {

    @Inject
    private TbPullNewDetailRepository tbPullNewDetailRepository;

    @Inject
    private UserRepository userRepository;

    @Inject
    private JdbcTemplate jdbcTemplate;

    @Inject
    private SystemSettingService systemSettingService;

    @Override
    @Transactional
    public void saveTbPullNewDetailList(List<TbPullNewDetailAddDTO> itemList) {
        List<TbPullNewDetail> details = new ArrayList<>();

        for (TbPullNewDetailAddDTO addDto : itemList) {

            TbPullNewDetail detail = tbPullNewDetailRepository.findByMobileAndRegisterTime(addDto.getMobile(), addDto.getRegisterTime());
            if (detail == null) {
                detail = new TbPullNewDetail();
            }
            BeanUtils.copyProperties(addDto, detail);
            User user = userRepository.findByTaobaoAdzoneId(addDto.getAdzoneId());
            if (user != null) {
                detail.setUserId(user.getId());
            }
            detail.setCreateTime(new Date());
            details.add(detail);
        }
        this.tbPullNewDetailRepository.save(details);
    }

    @Override
    public synchronized void saveTbPullNewDetailListSync(List<TbPullNewDetailAddDTO> itemList) {
        this.saveTbPullNewDetailList(itemList);
    }

    @Override
    public PullNewStat statUserByMonth(long userId, int year, int month) {
        String sql = "select `status`, count(1) as num from tb_pull_new_detail where user_id=? and register_time between ? and ? group by `status`";
        Date now;
        try {
            now = new SimpleDateFormat("yyyy-M-d").parse(year + "-" + month + "-" + "1");
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
        Date startTime = DateUtils.truncate(now, Calendar.MONTH);
        Date endTime = DateUtils.addSeconds(DateUtils.addMonths(startTime, 1), -1);
        return jdbcTemplate.query(sql, new Object[]{userId, startTime, endTime}, rs -> {
            return of(rs);
        });
    }

    @Override
    public PullNewStat statByUserId(long userId) {
        String sql = "select `status`, count(1) as num from tb_pull_new_detail where user_id=? group by `status`";
        return jdbcTemplate.query(sql, new Object[]{userId}, rs -> {
            return of(rs);
        });
    }

    @Override
    public Paging<TbPullNewDetailDTO> search(SearchParam param) {
        Specification<TbPullNewDetail> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (param.getUserId() != null) {
                expressions.add(cb.equal(root.get("userId"), param.getUserId()));
            }
            if (param.getStatus() != null) {
                expressions.add(cb.equal(root.get("status"), param.getStatus()));
            }
            if (param.getYear() != null && param.getMonth() != null) {
                try {
                    Date startTime = new SimpleDateFormat("yyyy-M-d").parse(param.getYear() + "-" + param.getMonth() + "-1");
                    Date endTime = DateUtils.addSeconds(DateUtils.addMonths(startTime, 1), -1);
                    expressions.add(cb.between(root.get("registerTime"), startTime, endTime));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
            return predicate;
        };
        Sort sort = new Sort(Sort.Direction.DESC, "registerTime");
        org.springframework.data.domain.Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<TbPullNewDetail> page = tbPullNewDetailRepository.findAll(specification, pageable);

        return PagingHelper.of(page, (uco) -> {
            TbPullNewDetailDTO dto = new TbPullNewDetailDTO();
            BeanUtils.copyProperties(uco, dto);
            return dto;
        }, param.getPage(), param.getPageSize());
    }

    private PullNewStat of(ResultSet rs) throws SQLException {
        PullNewStat stat = new PullNewStat();
        ActivitySetting activitySetting = systemSettingService.findActiveSetting(ActivitySetting.class);
        int lockedNum = 0;
        while (rs.next()) {
            lockedNum += rs.getInt("num");
            int status = rs.getInt("status");
            int num = rs.getInt("num");
            switch (status) {
                case 1:
                    //新注册
                    stat.setRegisterNum(num);
                    break;
                case 2:
                    //已激活
                    stat.setActivatedNum(num);
                    break;
                case 3:
                    //已购买
                    stat.setBoughtNum(num);
                    break;
                case 4:
                    //已完成
                    stat.setCompleteNum(num);
                    break;
            }
        }
        stat.setEstimatedRewards((stat.getBoughtNum() + stat.getCompleteNum())
                * activitySetting.getTbFullNewEachFee());
        stat.setLockedNum(lockedNum);
        return stat;
    }
}
